/*
 * @Description:顶点和中间点的图层
 */
import { latLng, LatLng } from 'leaflet';

import Layer from './layer';
import Feature from '../feature/feature';
import { FeatureJson, LayerOption } from '../types';
import Vertex from '../feature/vertex';
import Polygon from '../feature/polygon';
import Line from '../feature/line';
import { generateId, getAnotation } from '../util/util';

type VirtualVertex = {
    id: string;
    latLng: LatLng;
    relateFeatures: Feature[];
    attr?: { size?: number; className?: string };
    judgeRepeat: boolean;
};

class NodesLayer extends Layer {
    // todo allVertex的value应该是多个id
    allVertex: Map<string, string> = new Map(); // 用来判断顶点是否是交点，如果此经纬度已经上图，说明是交点,key是经纬度，value是id
    virtualVertex: Map<string, VirtualVertex> = new Map();

    constructor(layerOption: LayerOption) {
        super(layerOption, 'node');
        this.layerOption.featureClick = this.featureClick.bind(this);
        this.layerOption.featureMousemove = this.featureMousemove.bind(this);
        this.layerOption.featureMouseup = this.featureMouseup.bind(this);
        this.layerOption.addVertex = this.addVertex.bind(this);
        this.layerOption.nodesLayer = this;
    }

    /**
     * 添加线和面的顶点
     * @param latLng
     * @param relateFeatures 关联要素
     * @param attr
     * @param judgeRepeat 是否需要判断经纬度重复,   绘制时的虚线顶点不用判断，拆分后的交叉点不用判断
     * @param show 是否必显示，例如线的两个端点
     * @returns id
     */

    addVertex(
        latLng: LatLng,
        relateFeatures: Feature[],
        attr?: { size?: number; className?: string },
        judgeRepeat = true,
        show = false
    ): string {
        // 以经纬度为key,用于判重
        const key = latLng.lat + '_' + latLng.lng;
        const vertexId = this.allVertex.get(key); // 已经添加过同经纬度的点
        const id = judgeRepeat ? vertexId || generateId('node') : generateId('node'); // 不需要判重的顶点，生成新的id
        if (show) {
            return this.addActualVertex(id, latLng, relateFeatures, attr, judgeRepeat);
        } else if (vertexId && judgeRepeat) {
            const virtualParams = this.virtualVertex.get(vertexId);
            relateFeatures = relateFeatures.concat(virtualParams?.relateFeatures || []);
            attr = { ...attr, className: 'vertex cross' };
            return this.addActualVertex(id, latLng, relateFeatures, attr, judgeRepeat);
        } else {
            // 添加虚拟顶点
            const id = generateId('node');
            const virtual = { id, latLng, relateFeatures, attr, judgeRepeat };
            this.virtualVertex.set(id, virtual);
            if (judgeRepeat) {
                this.allVertex.set(key, id);
            }
            return this.addVirtualVertex(latLng, relateFeatures, attr, judgeRepeat);
        }
    }

    addActualVertex(
        vertexId: string,
        latLng: LatLng,
        relateFeatures: Feature[],
        attr?: { size?: number; className?: string },
        judgeRepeat = true
    ): string {
        // 以经纬度为key,用于判重
        const key = latLng.lat + '_' + latLng.lng;
        let vertex = <Vertex>this.features.get(vertexId);
        if (vertex && judgeRepeat) {
            // 渲染数据库要素时，需要判断是否已经添加过，添加过的点变为交叉点
            vertex = <Vertex>this.features.get(vertexId);

            vertex.relateFeatures = vertex ? vertex.relateFeatures.concat(relateFeatures) : relateFeatures;

            vertex.changeAttribute({ className: { vertex: true, cross: true, _hidden: false } });
        } else {
            vertex = new Vertex(vertexId, this.layerOption, latLng, {});
            this.add(vertex);
            vertex.addRelateFeatures(relateFeatures);
            if (attr) {
                vertex.changeAttribute(attr);
            }
        }

        if (judgeRepeat) {
            this.allVertex.set(key, vertex.id);
        }
        // 添加实际顶点后，把虚拟顶点集合的信息删除
        this.virtualVertex.delete(vertex.id);
        return vertex.id;
    }

    addVirtualVertex(
        latLng: LatLng,
        relateFeatures: Feature[],
        attr?: { size?: number; className?: string },
        judgeRepeat = true
    ) {
        const key = latLng.lat + '_' + latLng.lng;
        const id = generateId('node');
        const virtual = { id, latLng, relateFeatures, attr, judgeRepeat };
        this.virtualVertex.set(id, virtual);
        if (judgeRepeat) {
            this.allVertex.set(key, id);
        }
        return id;
    }

    changeVertexVisible(vertexId: string, visible: boolean) {
        const vertex = <Vertex>this.features.get(vertexId);
        const virtualVertex = this.virtualVertex.get(vertexId);

        if (vertex) {
            const intanceType = vertex.getInstanceType();
            vertex.changeAttribute({ className: { _hidden: intanceType === 'cross' ? false : !visible } });
        } else if (virtualVertex) {
            const { latLng, relateFeatures, attr, judgeRepeat } = virtualVertex;
            this.addActualVertex(vertexId, latLng, relateFeatures, attr, judgeRepeat);
        }
    }

    /**
     * 对顶点的点击事件，如果是绘制中且点击的是绘制要素的最后一个顶点，则结束绘制
     * 否则绘制要素添加个经纬度值，并把被点击的要素当作顶点
     * @param e
     * @param feature 被点击的顶点
     * @returns
     */
    featureClick(e: MouseEvent, feature: Feature) {
        // 线和顶点关联后，判断是否是线的最后一个点，如果是则结束绘制
        const vertexId = feature.id;
        const drawFeature = <Line | Polygon>this.layerOption.mode.drawFeature;
        if (!drawFeature) {
            return;
        }
        // 点击的是标绘中的线或者面的最后一个顶点，结束绘制
        const currSequenceNum = drawFeature.relateNodeIds.length - 1;
        const lastRelateId =
            drawFeature.relateNodeIds[currSequenceNum][drawFeature.relateNodeIds[currSequenceNum].length - 1];
        if (lastRelateId === vertexId) {
            // 结束绘制
            drawFeature.drawEnd();
        } else {
            // 点击了其他顶点，往标绘的线上添加一个经纬度，不用添加顶点，把此定点设置为关联点
            drawFeature.relateNodeIds[currSequenceNum].push(vertexId);
            drawFeature.addLatLng(feature.latLngs[currSequenceNum][0], false);

            this.layerOption.addHistory({
                annotation: getAnotation('draw cross', drawFeature),
                features: [drawFeature],
            });
            (feature as Vertex).relateFeatures.push(drawFeature);
            (feature as Vertex).changeAttribute({ className: 'vertex cross' });
        }
    }

    /**
     *  标绘模式下，feature上的mouse事件会被阻止冒泡，当鼠标在顶点要素上移动时，吸附顶点上
     * 拖拽模式，也要有吸附
     * @param e
     * @param feature 鼠标滑过的顶点要素
     */
    featureMousemove(e: MouseEvent, feature: Feature) {
        const modeType = this.layerOption.mode.getType();
        const drawFeature = <Line | Polygon>this.layerOption.mode.drawFeature;
        const selectFeature = <Vertex>this.layerOption.mode.selectedFeature;
        if (modeType === 'draw' && drawFeature) {
            drawFeature.drawMove(feature.latLngs[0][0]);
        } else if (modeType === 'drag' && selectFeature) {
            // feature 是要被吸附的顶点
            selectFeature.changeLatlng(feature.latLngs[0][0], [0]);
        }
    }

    /**
     * 顶点触发mouseup事件
     * @param e
     * @param feature 触发的顶点要素
     */
    featureMouseup(e: MouseEvent, feature: Feature) {
        const modeType = this.layerOption.mode.getType();
        const selectFeature = <Vertex>this.layerOption.mode.selectedFeature;
        if (modeType === 'drag' && selectFeature) {
            this.layerOption.changeMode('select', selectFeature);
            selectFeature.changeAttribute({ className: 'vertex cross' });
            // 把两个顶点的关联要素合并到一个顶点上
            selectFeature.addRelateFeatures((feature as Vertex).relateFeatures);
            // 更新feature所关联的线和面的关联id
            (feature as Vertex).relateFeatures.forEach((item) => {
                const { sequence, indexs } = (item as Line | Polygon).getNodeIndex(feature.id);
                if (!indexs.length) {
                    return;
                }
                indexs.forEach((relateIndex) => {
                    (item as Line | Polygon).relateNodeIds[sequence].splice(relateIndex, 1, selectFeature.id);
                });
            });
            // 删除被合并的顶点（不能调用remove方法，不然关联要素也会被删除）
            this.features.delete(feature.id);
            feature.group.remove();
        }
    }

    // 地图数据重新加载时，把缓存的顶点对象清除
    resetData() {
        this.allVertex = new Map();
    }

    /**
     *  删除顶点时维护allVertex，根据经纬度拼接成的key,判断allVertex是否存在，如果存在则删除
     * 如果不删除，下次无法添加相同经纬度点
     * @param key lat_lng
     */
    deleteJudgeRepeatItem(key: string) {
        if (this.allVertex.get(key)) {
            this.allVertex.delete(key);
        }
    }

    updateJudgeObject(key: string, nodeId: string) {
        if (!this.allVertex.get(key)) {
            this.allVertex.set(key, nodeId);
        }
    }

    /**
     * 重做操作，把删除的点重新添加到线或者面上
     * @param features
     */
    render(features: FeatureJson[]) {
        return [];
    }
}

export default NodesLayer;
